#!/usr/bin/perl

use strict;
use warnings;

use Carp ();
use Symbol::Util ();

$SIG{__WARN__} = sub { local $Carp::CarpLevel = 1; Carp::confess("Warning: ", @_) };

use Test::More tests => 38;

sub list_subroutines {
    no strict 'refs';
    my ($name) = @_;
    return [ sort grep { is_code("${name}::$_") }
                  keys %{ *{"${name}::"} } ];
};

sub is_code {
    my ($name) = @_;
    no strict 'refs';
    return defined *{$name}{CODE};
};

sub is_constant {
    my ($name) = @_;
    my $prototype = prototype $name;
    return is_code($name) && defined $prototype && $prototype eq '';
};

eval q{
    package namespace::globref::Test10::Package1;
    sub a {"a"}
    sub b {"b"}
    sub c {"c"}
};
is($@, '', 'Package1 is ok');
is_deeply( list_subroutines("namespace::globref::Test10::Package1"), [qw{ a b c }], 'namespace is ok' );
ok( is_code("namespace::globref::Test10::Package1::a"), 'a is sub' );
ok( !is_constant("namespace::globref::Test10::Package1::a"), 'a is not constant' );
is( eval {namespace::globref::Test10::Package1->a}, "a", 'a returns a' );
ok( is_code("namespace::globref::Test10::Package1::b"), 'b is sub' );
ok( !is_constant("namespace::globref::Test10::Package1::b"), 'b is not constant' );
is( eval {namespace::globref::Test10::Package1->b}, "b", 'b returns b' );
ok( is_code("namespace::globref::Test10::Package1::c"), 'c is sub' );
ok( !is_constant("namespace::globref::Test10::Package1::c"), 'c is not constant' );
is( eval {namespace::globref::Test10::Package1->c}, "c", 'c returns c' );

eval q{
    package namespace::globref::Test10::Package2;
    sub a {"a"}
    sub b {"b"}
    use namespace::globref;
    sub c {"c"}
};
is($@, '', 'Package2 is ok');
is_deeply( list_subroutines("namespace::globref::Test10::Package2"), [qw{ a b c }], 'namespace is ok' );
ok( is_code("namespace::globref::Test10::Package2::a"), 'a is sub' );
ok( is_constant("namespace::globref::Test10::Package2::a"), 'a is constant' );
is( eval {namespace::globref::Test10::Package2->a->()}, "a", 'a->() returns a' );
ok( is_code("namespace::globref::Test10::Package2::b"), 'b is sub' );
ok( is_constant("namespace::globref::Test10::Package2::b"), 'b is constant' );
is( eval {namespace::globref::Test10::Package2->b->()}, "b", 'b->() returns b' );
ok( is_code("namespace::globref::Test10::Package2::c"), 'c is sub' );
ok( !is_constant("namespace::globref::Test10::Package2::c"), 'c is not constant' );
is( eval {namespace::globref::Test10::Package1->c}, "c", 'c returns c' );

eval q{
    package namespace::globref::Test10::Package3;
    sub a {"a"}
    sub b {"b"}
    use namespace::globref;
    sub c {"c"}
    no namespace::globref;
};
is($@, '', 'Package3 is ok');
is_deeply( list_subroutines("namespace::globref::Test10::Package3"), [qw{ c }], 'namespace is ok' );
ok( !is_code("namespace::globref::Test10::Package3::a"), 'a is undef' );
ok( !is_code("namespace::globref::Test10::Package3::b"), 'b is undef' );
ok( is_code("namespace::globref::Test10::Package3::c"), 'c is sub' );
ok( !is_constant("namespace::globref::Test10::Package3::c"), 'c is not constant' );
is( eval {namespace::globref::Test10::Package3->c}, "c", 'c returns c' );

eval q{
    package namespace::globref::Test10::Package4;
    sub a {"a"}
    sub b {"b"}
    use namespace::globref 'a', 'b';
    sub c {"c"}
    no namespace::globref 'b';
};
is($@, '', 'Package4 is ok');
is_deeply( list_subroutines("namespace::globref::Test10::Package4"), [qw{ a c }], 'namespace is ok' );
ok( is_code("namespace::globref::Test10::Package4::a"), 'a is code' );
ok( is_constant("namespace::globref::Test10::Package4::a"), 'a is constant' );
is( eval {namespace::globref::Test10::Package4->a->()}, "a", 'a->() returns a' );
ok( !is_code("namespace::globref::Test10::Package4::b"), 'b is undef' );
ok( is_code("namespace::globref::Test10::Package4::c"), 'c is sub' );
ok( !is_constant("namespace::globref::Test10::Package4::c"), 'c is not constant' );
is( eval {namespace::globref::Test10::Package4->c}, "c", 'c returns c' );
